Skip to content

fix(ui): resolve root-relative README markdown links to repo blob URLs#2929

Open
BittuBarnwal7479 wants to merge 5 commits into
npmx-dev:mainfrom
BittuBarnwal7479:codex/fix-readme-root-relative-md-links
Open

fix(ui): resolve root-relative README markdown links to repo blob URLs#2929
BittuBarnwal7479 wants to merge 5 commits into
npmx-dev:mainfrom
BittuBarnwal7479:codex/fix-readme-root-relative-md-links

Conversation

@BittuBarnwal7479

@BittuBarnwal7479 BittuBarnwal7479 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

🔗 Linked issue

#2928

🧭 Context

Steps to Reproduce

  1. Open https://npmx.dev/package/astro
  2. Find a README link that points to /CONTRIBUTING.md
  3. Click the link
Recording.2026-06-17.174239.mp4

📚 Description

On package readme.ts page, root-relative markdown links such as /CONTRIBUTING.md are resolved as local npmx routes instead of repository files.

@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Jun 17, 2026 9:40pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Jun 17, 2026 9:40pm
npmx-lunaria Ignored Ignored Jun 17, 2026 9:40pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d54a9330-8917-4102-9a57-80c407e4e5f0

📥 Commits

Reviewing files that changed from the base of the PR and between a0b738c and 03ae304.

📒 Files selected for processing (2)
  • server/utils/readme.ts
  • test/unit/server/utils/readme.spec.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/unit/server/utils/readme.spec.ts
  • server/utils/readme.ts

📝 Walkthrough

Summary by CodeRabbit

  • Style

    • Improved the “Badge Style” selector styling for clearer light/dark text and tidier option formatting.
  • Bug Fixes

    • Enhanced README link URL resolution, including correct handling of root-relative paths, choosing blob vs raw URLs for markdown, and improved behaviour for npm-style redirect links.
  • Tests

    • Expanded unit coverage for markdown file URL resolution with repository context, including raw HTML link cases and ensuring protocol-relative URLs remain unchanged.

Walkthrough

server/utils/readme.ts gains a LOCAL_NPMX_REDIRECT_PREFIX constant and toLocalNpmxRedirect() helper to mark locally-resolved redirects. resolveUrl() now strips this prefix immediately, computes markdown detection earlier, and conditionally rewrites root-relative paths to repository blob or raw URLs. Redirectable npmjs URLs are wrapped with the prefix instead of returning the pathname directly. 72 lines of unit tests cover all new branches. BadgeGeneratorParameters.vue receives minor Tailwind dark-mode class additions on the Badge Style dropdown.

Changes

Root-relative URL resolution fix

Layer / File(s) Summary
Local redirect prefix constant and toLocalNpmxRedirect() wrapper
server/utils/readme.ts
Introduces LOCAL_NPMX_REDIRECT_PREFIX constant and toLocalNpmxRedirect(path) helper to wrap locally-resolved paths with the $npmx-local: prefix for deferred handling by resolveUrl().
resolveUrl() prefix stripping and URL rewriting refactor
server/utils/readme.ts
Updates resolveUrl() to immediately strip LOCAL_NPMX_REDIRECT_PREFIX, compute isMarkdownFile earlier, conditionally rewrite absolute-path URLs based on repoInfo presence and markdown status (using blobBaseUrl for .md, rawBaseUrl for non-markdown), return npmjs redirects via toLocalNpmxRedirect(), and remove the now-duplicate isMarkdownFile computation.
Unit tests for new URL resolution branches
test/unit/server/utils/readme.spec.ts
Adds "with repository info" cases: root-relative .md links (including raw HTML <a> tags) resolve to the repository blob URL; root-relative non-.md links resolve to the raw URL; /package/... paths are resolved to repository raw URL; www.npmjs.com/package/... redirects remain local as /package/...; route-root paths (/package, /org) stay local; protocol-relative URLs remain unchanged.

Badge Style dropdown dark-mode styling

Layer / File(s) Summary
Badge Style select Tailwind classes
docs/app/components/BadgeGeneratorParameters.vue
<select> element gains explicit text colour classes for light and dark themes; each <option> element gains dark:bg-gray-900 with reformatted markup.

Possibly related issues

  • root-relative markdown links resolve to npmx host #2928: The changes in this PR directly address this issue by modifying resolveUrl() in server/utils/readme.ts to detect and handle local NPMX paths using the new prefix marking system, correctly rewriting root-relative markdown links (for example /CONTRIBUTING.md) to repository blob URLs rather than treating them as npmx host routes.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title accurately reflects the main change: fixing resolution of root-relative markdown links in README files to point to repository blob URLs rather than local npmx routes.
Description check ✅ Passed The description is directly related to the changeset, providing context about the bug (root-relative markdown links being incorrectly resolved), the steps to reproduce it, and the intended fix.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 63.63636% with 4 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
server/utils/readme.ts 63.63% 1 Missing and 3 partials ⚠️

📢 Thoughts on this report? Let us know!

@BittuBarnwal7479 BittuBarnwal7479 changed the title Resolve root-relative README markdown links to repo blob URLs fix(ui): resolve root-relative README markdown links to repo blob URLs Jun 17, 2026
@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

An error occurred during the review process. Please try again later.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

const result = await renderReadmeHtml(markdown, 'test-pkg', repoInfo)

expect(result.html).toContain('href="/package/test-pkg"')
})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why we want this behaviour for non-markdown links?

Why not something similar to what is done for this?

it('resolves non-.md files to raw URL (not blob)', async () => {
const repoInfo = createRepoInfo()
const markdown = `[Image](./assets/logo.png)`
const result = await renderReadmeHtml(markdown, 'test-pkg', repoInfo)
expect(result.html).toContain(
'href="https://raw.githubusercontent.com/test-owner/test-repo/HEAD/assets/logo.png"',
)
})

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I updated this to resolve root-relative non-markdown links via rawBaseUrl, consistent with existing relative-link handling. Markdown files still resolve via blobBaseUrl.

Local npmx routes (/package, /org, /search, etc.) are preserved separately, and I added regression tests covering both behaviors.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason / place where preserving Local npmx routes would be useful instead of them resolving to rawBaseUrl too?

@BittuBarnwal7479 BittuBarnwal7479 Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. the main case is npmjs links that the README renderer intentionally converts to local npmx routes. For example, https://www.npmjs.com/package/test-pkg becomes /package/test-pkg.

If we treated every root-relative path as a repo file, that converted route would incorrectly become rawBaseUrl/package/test-pkg.

So the updated logic preserves known npmx routes separately, while root-relative repo files like /CONTRIBUTING.md and /assets/logo.png resolve to blobBaseUrl / rawBaseUrl.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, is there no way to differentiate a /package that was because of https://www.npmjs.com/package/test-pkg from a /package that someone wrote in their readme?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. i will update this to preserve npmjs-originated links via an internal marker rather than path matching. README-authored root-relative paths now resolve normally against the repository. Added regression tests for both cases.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/utils/readme.ts (1)

352-358: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep protocol-relative URLs out of the root-relative rewrite.

//cdn.example.com/file.css also satisfies url.startsWith('/'), so with repository info it becomes https://raw.githubusercontent.com/.../HEAD//cdn.example.com/file.css instead of staying external. The later protocol-relative handling never gets a chance to run.

Proposed fix
-  if (url.startsWith('/')) {
+  if (url.startsWith('/') && !url.startsWith('//')) {
     if (!repoInfo?.rawBaseUrl) {
       return url
     }
 
     const baseUrl = isMarkdownFile ? repoInfo.blobBaseUrl : repoInfo.rawBaseUrl
     return `${baseUrl}${url}`
   }

Please add a regression case such as [CDN](//cdn.example.com/file.css) with repoInfo and assert the href remains protocol-relative.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/utils/readme.ts` around lines 352 - 358, The root-relative URL check
at the beginning of the conditional block (url.startsWith('/')) is incorrectly
matching protocol-relative URLs that start with //, causing them to be rewritten
as repository URLs instead of remaining external. Add an additional condition to
explicitly exclude protocol-relative URLs (those starting with //) before
processing the root-relative URL case in the repoInfo block. Additionally, add a
regression test case that verifies a markdown link with a protocol-relative URL
like [CDN](//cdn.example.com/file.css) correctly preserves the protocol-relative
href when repoInfo is present.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@server/utils/readme.ts`:
- Around line 352-358: The root-relative URL check at the beginning of the
conditional block (url.startsWith('/')) is incorrectly matching
protocol-relative URLs that start with //, causing them to be rewritten as
repository URLs instead of remaining external. Add an additional condition to
explicitly exclude protocol-relative URLs (those starting with //) before
processing the root-relative URL case in the repoInfo block. Additionally, add a
regression test case that verifies a markdown link with a protocol-relative URL
like [CDN](//cdn.example.com/file.css) correctly preserves the protocol-relative
href when repoInfo is present.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e84a9795-dd1d-41b7-b64c-33fc7a943e87

📥 Commits

Reviewing files that changed from the base of the PR and between 5e7897b and a0b738c.

📒 Files selected for processing (2)
  • server/utils/readme.ts
  • test/unit/server/utils/readme.spec.ts

@BittuBarnwal7479

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review again.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

@BittuBarnwal7479 Sure! I'll kick off a new review of the PR right away.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants